/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2019年1月21日
 * V4.0
 */
package com.jphenix.kernel.script;

import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanRegister;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.ClassUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.script.IScriptBean;
import com.jphenix.standard.script.IScriptLoader;
import com.jphenix.standard.threadpool.ICrontabBean;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 脚本对象外壳
 * 
 * 2019-02-11 实现了接口IBeanRegister
 * 2019-02-11 实现了接口ICrontabBean
 * 2019-04-09 增加了获取脚本对应的ScriptVO类实例方法
 * 2019-04-10 在当前线程中设置了上下文类加载器，避免通过上下文类加载器无法获取到指定资源
 * 2019-06-13 按照新的ScriptFieldVO传参修改了代码
 * 2019-07-08 增加了调用报错时，打印类加载器中的扩展包路径信息，以便于排查错误。
 * 2019-07-20 适应ScriptVO中的绝对路径改为相对路径
 * 2020-01-07 增加了是否执行该定时任务的开关
 * 2020-09-09 按照接口增加了暂不支持无用的方法
 * 
 * @author MBG
 * 2019年1月21日
 */
@ClassInfo({"2020-09-09 14:58", "脚本对象外壳"})
public class ScriptWrapper extends ABase implements IScriptBean,IBeanRegister,ICrontabBean {

	protected ScriptClassLoader   scl       = null;  //带专用包路径的子类加载器
	protected ScriptVO            sVO       = null;  //脚本信息类
	private   Object              kernel    = null;  //核心脚本类实例
	private   List<ScriptFieldVO> pList     = null;  //脚本参数信息序列
	private   List<String>        fList     = null;  //当前脚本被其它脚本调用，其它脚本主键序列
	private   boolean             isEnabled = true;  //是否执行该定时任务
	  
	/**
	 * 构造函数
	 * @author MBG
	 */
	public ScriptWrapper(ScriptVO sVO) throws Exception {
		super();
		this.sVO = sVO;
		if(!sVO.haveExtLibPaths) {
			//不存在走到这里
			throw new Exception("The Script:["+sVO.id+"] Not Have ExtLibPaths Value,Do Not Use ScriptWrapper");
		}
		if(sVO.nativeScript) {
			throw new Exception("The Script:["+sVO.id+"] ["+StringUtil.arr2str(sVO.extLibPaths)+"] Is NativeScript,Do Not Use ScriptWrapper.Dont't Play Stupid !!!");
		}
		
		setBase(sVO.sl);       //设置基类
		this._beanID = sVO.id; //设置类主键，类主键无法从基类方法中设置
		
		//构建带专用包路径的子类加载器
		scl = new ScriptClassLoader(sVO.sl.getClassLoader(),sVO.sl,sVO.extLibPaths);
		
		
		//注意：jar包中不允许使用外部扩展包，因为设计框架的思路就是独立运行。如果内部脚本中使用了扩展包，就意味着框架包必须跟扩展包一起加载，切记
		
		//当前脚本的类文件序列（包含这个脚本类内部的类）
		List<String> pathList = new ArrayList<String>();
		
		//类文件名（不包含扩展名）内部类的名字开头也是这个值
		SFilesUtil.getFileList(
				 pathList
				,SFilesUtil.getFilePath(sVO.sl.getClassBasePath()+sVO.classFilePath)
				,SFilesUtil.getFileBeforeName(sVO.classFilePath)
				,"class",false,false);
		for(String path:pathList) {
			scl.addClassPath(path);
		}
		//设置上下文类加载器
		Thread.currentThread().setContextClassLoader(scl);
	}

	
	/**
	 * 覆盖方法
	 */
	@Override
	public List<ScriptFieldVO> getParameterList() {
		if(pList==null) {
			pList = new ArrayList<ScriptFieldVO>();
			for(ScriptFieldVO sfVO:sVO.inFields) {
				pList.add(new ScriptFieldVO(
						 sfVO.name
						,sfVO.type
						,sfVO.defValue
						,BaseUtil.swapString(sfVO.explain,"\r","","\n","","\"","\\\"")
						,Boolean.valueOf(sfVO.notNull)
						,Boolean.valueOf(sfVO.isStaticPara)
						,Boolean.valueOf(sfVO.needB64Dec)));
			}
		}
		return pList;
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public String getScriptId() {
		return sVO.id;
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public List<String> getForScriptIds() {
		if(fList==null) {
			fList = new ArrayList<String>();
			//获取调用该脚本的其他脚本序列
			List<ScriptVO> sList = sVO.sl.getForInclude(sVO.id);
			for(ScriptVO ele:sList){
				fList.add(ele.id);
			}
		}
		return fList;
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public IScriptLoader getScriptLoader() {
		return sVO.sl;
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public boolean noOutLog() {
		return sVO.noOutLog;
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public boolean isDbTransaction() {
		return sVO.isDbTransaction;
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public boolean isEnabledActionCharge() {
		return sVO.enabledActionCharge;
	}

	
	/**
	 * 获取核心脚本类实例
	 * @return 核心脚本类实例
	 * 2019年1月22日
	 * @author MBG
	 */
	private Object getKernel() {
		if(kernel==null) {
			try {
				//构建核心脚本类u第项
				Class<?> cls = scl.loadClass(sVO);
				//构建类实例
	            Constructor<?> scriptCon = cls.getDeclaredConstructor(IScriptLoader.class,ScriptVO.class);
	            
	            //构造类实例
	            kernel = scriptCon.newInstance(sVO.sl,sVO);
	            
	    		if(kernel instanceof ABase) {
	    			((ABase)kernel).setBase(sVO.sl);
	    		}
	            
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		return kernel;
	}
	
	/**
	 * 核心类是否实现了指定接口
	 * @param cls     指定接口
	 * @return        是否实现了指定接口
	 * 2019年1月23日
	 * @author MBG
	 */
	public boolean implementsClass(Class<?> cls) {
		return ClassUtil.implementsClass(getKernel().getClass(),cls);
	}
	
	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public void _executeN(Map<String,?> para) throws Exception {
		Object kernel = getKernel(); //获取核心脚本类实例
		Method method = kernel.getClass().getMethod("_executeN", Map.class);
		try {
			method.invoke(kernel, para); //调用核心脚本方法
		}catch(Exception e) {
			error("_executeN The Script:["+sVO.id+"] Exception:"+e+"\nScriptLoader:\n"+scl.toString());
			throw e;
		}
	}

	/**
	 * 覆盖方法
	 * 2019年1月21日
	 * @author MBG
	 */
	@Override
	public Object _execute(Map<String,?> para) throws Exception {
		Object kernel = getKernel(); //获取核心脚本类实例
		Method method = kernel.getClass().getMethod("_execute", Map.class);
		try {
			return method.invoke(kernel, para); //调用核心脚本方法
		}catch(Exception e) {
			error("_execute The Script:["+sVO.id+"] Exception:"+e+"\nScriptLoader:\n"+scl.toString());
			throw e;
		}
	}
	
	/**
	 * 运行核心脚本方法，比如初始化方法
	 * @param methodName  方法名
	 * @throws Exception  异常
	 * 2019年1月23日
	 * @author MBG
	 */
	public void runMethod(String methodName) throws Exception {
		Object kernel = getKernel(); //获取核心脚本类实例
		Method method = kernel.getClass().getMethod(methodName);
		try {
			method.invoke(kernel); //调用核心脚本方法
		}catch(Exception e) {
			error("runMethod1 The Script:["+sVO.id+"] Exception:"+e+"\nScriptLoader:\n"+scl.toString());
			throw e;
		}
	}
	
	/**
	 * 运行核心脚本方法，比如注册方法
	 * @param methodName   方法名
	 * @param clss         传参类型对象数组
	 * @param paras        传入值数组
	 * @return             返回值
	 * @throws Exception   异常
	 * 2019年1月23日
	 * @author MBG
	 */
	public Object runMethod(String methodName,Class<?>[] clss,Object[] paras) throws Exception {
		Object kernel = getKernel(); //获取核心脚本类实例
		Method method = kernel.getClass().getMethod(methodName,clss);
		try {
			return method.invoke(kernel,paras);
		}catch(Exception e) {
			error("runMethod2 The Script:["+sVO.id+"] Exception:"+e+"\nScriptLoader:\n"+scl.toString());
			throw e;
		}
	}
	
	
	/**
	 * 如果核心类实现了接口 IBeanRegister，需要执行该方法
	 * @param bean   被注册的类实例
	 * @return       注册结果
	 * 2019年2月11日
	 * @author MBG
	 */
	@Override
	public boolean regist(Object bean) {
		try {
			return boo(runMethod("regist",new Class[] {Object.class},new Object[] {bean}));
		}catch(Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * 如果核心类实现了接口 IBeanRegister，需要执行该方法
	 * @param bean   被反注册的类实例
	 * 2019年2月11日
	 * @author MBG
	 */
	@Override
	public void unRegist(Object bean) {
		try {
			runMethod("unRegist",new Class[] {Object.class},new Object[] {bean});
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

    /**
     * 获取定时间隔时间信息
     * @return 定时间隔时间信息
     * 2014年10月20日
     * @author 马宝刚
     */
	@Override
	public String getTimeInfo() {
		try {
			return str(runMethod("getTimeInfo",new Class[0],new Object[0]));
		}catch(Exception e) {
			e.printStackTrace();
		}
		return "";
	}

    /**
     * 需要执行的方法
     * 2014年10月20日
     * @throws Exception 异常
     * @author 马宝刚
     */
	@Override
	public void run() throws Exception {
		try {
			runMethod("run",new Class[0],new Object[0]);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 覆盖方法
	 * 获取当前脚本对应的ScriptVO类实例
	 */
	@Override
	public ScriptVO getScriptVO() {
		return sVO;
	}


    /**
     * 是否允许执行该定时任务
     * @return 是否允许执行该定时任务
     * 2020年1月7日
     * @author MBG
     */
	@Override
	public boolean enabled() {
		return isEnabled;
	}


    /**
     * 设置是否允许指定该定时任务
     * @param isEnabled 是否允许指定该定时任务
     * 2020年1月7日
     * @author MBG
     */
	@Override
	public void enabled(boolean isEnabled) {
		this.isEnabled = isEnabled;
	}


	/**
	 * 扩展脚本类暂不支持传入参数变量设置为类变量模式
	 */
	@Override
	public boolean isClassVar() {
		return false;
	}
}
